#!/usr/bin/env python
import sys
import shutil
import threading
import time
import getch
from ptyprocess import PtyProcessUnicode
from multiprocessing import Process, Lock, Queue
import socket
from typing import Optional, List, Tuple, Dict

import readchar as readchar
import typer
from rich import print
from rich.columns import Columns
from rich.console import Console
from rich.traceback import install

# fmt: off
# Mapping from topics to colors
TOPICS = {
    "TIMR": "#9a9a99",
    "VOTE": "#67a0b2",
    "LEAD": "#d0b343",
    "TERM": "#70c43f",
    "LOG1": "#4878bc",
    "LOG2": "#398280",
    "CMIT": "#98719f",
    "PERS": "#d08341",
    "SNAP": "#FD971F",
    "DROP": "#ff615c",
    "CLNT": "#00813c",
    "TEST": "#fe2c79",
    "INFO": "#ffffff",
    "WARN": "#d08341",
    "ERRO": "#fe2626",
    "TRCE": "#fe2626",
}


# fmt: on


def list_topics(value: Optional[str]):
    if value is None:
        return value
    topics = value.split(",")
    for topic in topics:
        if topic not in TOPICS:
            raise typer.BadParameter(f"topic {topic} not recognized")
    return topics


'''
class RecvThread(threading.Thread):
    def __init__(self,que):
        threading.Thread.__init__(self)
        self.que = que
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.socket.bind(("127.0.0.1", 2333))
        print("start log server")
    def run(self):
        while True:
            data, addr = self.socket.recvfrom(1024)
            self.que.put(data.decode())

'''


def recv(que):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.bind(("127.0.0.1", 2333))
    print("start listen to logs")
    try:
        while True:
            data, addr = s.recvfrom(1024)
            line = data.decode()
            que.put(line)
    except Exception as e:
        print(e)
        s.close()


def run(que, infoque, colorize,
        n_columns,
        ignore,
        just,
        ):
    topics = list(TOPICS)
    # We can take input from a stdin (pipes) or from a file
    # Print just some topics or exclude some topics (good for avoiding verbose ones)
    if just:
        topics = just
    if ignore:
        topics = [lvl for lvl in topics if lvl not in set(ignore)]

    topics = set(topics)
    console = Console()
    width = console.size.width

    panic = False
    lstisnotformat = False

    traceList = {}
    try:
        pause = True
        ctn = False
        while True:

            while not que.empty():
                v = que.get()
                if v[0] == 1:
                    pause = not pause
                elif v[0] == 2:
                    pause = True
                ctn = v[1]
                if not v[2] is None:
                    print(v[2])
                    if v[2][1] == True:
                        traceList.add(v[2][0])
                    else:
                        traceList.remove(v[2][0])

            # print("pause is {},ctn is {}".format(pause,ctn))
            if pause:
                if not ctn:
                    time.sleep(0.01)
                    continue
                else:
                    ctn = False
            if infoque.empty():
                continue
            line = infoque.get()

            try:
                t, topic, *msg = line.strip().split(" ")

                if not (t.isdigit() or topic == "TEST"):
                    raise Exception

                if topic in traceList:
                    pause = True
                lstisnotformat = False
                # To ignore some topics
                if topic not in topics:
                    continue

                msg = " ".join(msg)

                # Debug calls from the test suite aren't associated with
                # any particular peer. Otherwise we can treat second column
                # as peer id
                if topic != "TEST":
                    i = int(msg[1])

                # Colorize output by using rich syntax when needed
                if colorize and topic in TOPICS:
                    color = TOPICS[topic]
                    msg = f"[{color}]{msg}[/{color}]"

                # Single column printing. Always the case for debug stmts in tests
                if n_columns is None or topic == "TEST":
                    print(t, msg)
                # Multi column printing, timing is dropped to maximize horizontal
                # space. Heavylifting is done through rich.column.Columns object
                else:
                    cols = ["" for _ in range(n_columns)]
                    msg = "" + msg
                    cols[i % n_columns] = msg
                    cols = [t + topic + " " + str(infoque.qsize()) + " " + str(traceList)] + cols
                    col_width = int(width / (n_columns + 1))
                    cols = Columns(cols, width=col_width - 1, equal=True, expand=True)
                    print(cols)
            except:
                # Code from tests or panics does not follow format
                # so we print it as is
                if line.startswith("panic"):
                    panic = True
                # Output from tests is usually important so add a
                # horizontal line with hashes to make it more obvious
                if not panic and not lstisnotformat:
                    print("#" * console.width)
                print(line, end="")
                lstisnotformat = True
    except Exception as e:
        print(e)


def main(
        # file: typer.FileText = typer.Argument(None, help="File to read, stdin otherwise"),
        colorize: bool = typer.Option(True, "--no-color"),
        n_columns: Optional[int] = typer.Option(None, "--columns", "-c"),
        ignore: Optional[str] = typer.Option(None, "--ignore", "-i", callback=list_topics),
        just: Optional[str] = typer.Option(None, "--just", "-j", callback=list_topics),
):
    # que = Queue()
    # recv = RecvThread(que)
    # recv.run()

    que = Queue()
    infoque = Queue()
    p = Process(target=run, args=(que, infoque, colorize, n_columns, ignore, just))
    p2 = Process(target=recv, args=(infoque,))
    p.start()
    p2.start()


    while True:

        c = getch.getch()
        cmd = None
        ctn = False
        flag = 0
        if c == "w" or c == "W":
            flag = 1

        elif c == "s" or c == "S":
            ctn = True
        elif c == "p" or c == "P":
            que.put((2, False, None))
            v = input()
            if v.strip() in TOPICS:
                cmd = (v, True)
            else:
                print("wrong topic:"+v)

        elif c == "x" or c == "X":
            print("exit")
            p.kill()
            p2.kill()
            exit(0)
        else:
            continue
        # if flag:
        # print("###pause is {}###".format(pause))

        que.put((flag, ctn, cmd))
        time.sleep(0.01)


if __name__ == "__main__":
    typer.run(main)
